#include "Engine.h"
#include <WindowsX.h>

#include "Assets/GeometryGenerator.h"
#include "RenderCore/Shader.h"
#include "Utils/D3DUtils.h"

#if defined(DEBUG) || defined(_DEBUG)
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif

using Microsoft::WRL::ComPtr;

const int GNumFrameResources = 3;

class FStandardVS : public FGlobalShader
{
public:
	FStandardVS(const Microsoft::WRL::ComPtr<ID3DBlob>& InShaderBlob)
		:  FGlobalShader(InShaderBlob)
	{}
};
IMPLEMENT_GLOBAL_SHADER(FStandardVS, "Shaders/Color.hlsl", "VS", SF_Vertex);

class FOpaquePS : public FGlobalShader
{
public:
	FOpaquePS(const Microsoft::WRL::ComPtr<ID3DBlob>& InShaderBlob)
		:  FGlobalShader(InShaderBlob)
	{}
};
IMPLEMENT_GLOBAL_SHADER(FOpaquePS, "Shaders/Color.hlsl", "PS", SF_Pixel);

LRESULT CALLBACK MainWndProc(HWND InWindow, UINT InMsg, WPARAM InWParam, LPARAM InLParam)
{
    // Forward hwnd on because we can get messages (e.g., WM_CREATE)
    // before CreateWindow returns, and thus before mhMainWnd is valid.
    return FEngine::Get()->MsgProc(InWindow, InMsg, InWParam, InLParam);
}

FEngine::~FEngine()
{
	if (mD3DDevice != nullptr)
	{
		FlushCommandQueue();
	}
}

void FEngine::Run(HINSTANCE InInstance)
{
    // Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif
	
    try
    {
        mAppInst = InInstance;
        if (Initialize())
        {
            Tick();
        }

        Release();
    }
    catch(DxException& e)
    {
        MessageBox(nullptr, e.ToString().c_str(), L"HR Failed", MB_OK);
    }
}

bool FEngine::Get4xMsaaState() const
{
	return b4xMSAAState;
}

void FEngine::Set4xMsaaState(bool bInState)
{
	if (b4xMSAAState != bInState)
	{
		b4xMSAAState = bInState;

		// Recreate the swapchain and buffers with new multisample settings.
		CreateSwapChain();
		OnResize();
	}
}

LRESULT FEngine::MsgProc(HWND InWindow, UINT InMsg, WPARAM InWParam, LPARAM InLParam)
{
    switch (InMsg)
	{
	// WM_ACTIVATE is sent when the window is activated or deactivated.  
	// We pause the engine when the window is deactivated and unpause it 
	// when it becomes active.  
	case WM_ACTIVATE:
		if (LOWORD(InWParam) == WA_INACTIVE)
		{
			bEnginePaused = true;
			mTimer.Stop();
		}
		else
		{
			bEnginePaused = false;
			mTimer.Start();
		}
		return 0;

	// WM_SIZE is sent when the user resizes the window.  
	case WM_SIZE:
		// Save the new window area dimensions.
		mWindowHeight = LOWORD(InLParam);
		mWindowHeight = HIWORD(InLParam);
		if (mD3DDevice)
		{
			if (InWParam == SIZE_MINIMIZED)
			{
				bEnginePaused = true;
				bMinimized = true;
				bMaximized = false;
			}
			else if (InWParam == SIZE_MAXIMIZED)
			{
				bEnginePaused = false;
				bMinimized = false;
				bMaximized = true;
				OnResize();
			}
			else if (InWParam == SIZE_RESTORED)
			{
				if (bMinimized)
				{
					// Restoring from minimized state
					bEnginePaused = false;
					bMinimized = false;
					OnResize();
				}
				else if (bMaximized)
				{
					// Restoring from maximized state
					bEnginePaused = false;
					bMaximized = false;
					OnResize();
				}
				else if (bResizing)
				{
					// If user is dragging the resize bars, we do not resize 
					// the buffers here because as the user continuously 
					// drags the resize bars, a stream of WM_SIZE messages are
					// sent to the window, and it would be pointless (and slow)
					// to resize for each WM_SIZE message received from dragging
					// the resize bars.  So instead, we reset after the user is 
					// done resizing the window and releases the resize bars, which 
					// sends a WM_EXITSIZEMOVE message.
				}
				else // API call such as SetWindowPos or mSwapChain->SetFullscreenState.
				{
					OnResize();
				}
			}
		}
		return 0;

	// WM_EXITSIZEMOVE is sent when the user grabs the resize bars.
	case WM_ENTERSIZEMOVE:
		bEnginePaused = true;
		bResizing = true;
		mTimer.Stop();
		return 0;

	// WM_EXITSIZEMOVE is sent when the user releases the resize bars.
	// Here we reset everything based on the new window dimensions.
	case WM_EXITSIZEMOVE:
		bEnginePaused = false;
		bResizing = false;
		mTimer.Start();
		OnResize();
		return 0;
 
	// WM_DESTROY is sent when the window is being destroyed.
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	// The WM_MENUCHAR message is sent when a menu is active and the user presses 
	// a key that does not correspond to any mnemonic or accelerator key. 
	case WM_MENUCHAR:
        // Don't beep when we alt-enter.
        return MAKELRESULT(0, MNC_CLOSE);

	// Catch this message so to prevent the window from becoming too small.
	case WM_GETMINMAXINFO:
		((MINMAXINFO*)InLParam)->ptMinTrackSize.x = 200;
		((MINMAXINFO*)InLParam)->ptMinTrackSize.y = 200; 
		return 0;

	case WM_LBUTTONDOWN:
	case WM_MBUTTONDOWN:
	case WM_RBUTTONDOWN:
		OnMouseDown(InWParam, GET_X_LPARAM(InLParam), GET_Y_LPARAM(InLParam));
		return 0;
	case WM_LBUTTONUP:
	case WM_MBUTTONUP:
	case WM_RBUTTONUP:
		OnMouseUp(InWParam, GET_X_LPARAM(InLParam), GET_Y_LPARAM(InLParam));
		return 0;
	case WM_MOUSEMOVE:
		OnMouseMove(InWParam, GET_X_LPARAM(InLParam), GET_Y_LPARAM(InLParam));
		return 0;
    case WM_KEYUP:
        if(InWParam == VK_ESCAPE)
        {
            PostQuitMessage(0);
        }
        else if((int)InWParam == VK_F2)
            Set4xMsaaState(!b4xMSAAState);

        return 0;
	}

	return DefWindowProc(InWindow, InMsg, InWParam, InLParam);
}

bool FEngine::Initialize()
{
    if (!InitMainWindow())
    {
        return false;
    }

	if (!InitDirect3D())
	{
		return false;
	}
	
	OnResize();
    
    bInitialized = true;

	// 重置命令列表为执行初始化命令做好准备工作
	ThrowIfFailed(mCommandList->Reset(mCommandListAlloc.Get(), nullptr));

	
	BuildRootSignature();
	BuildInputLayout();
	BuildShapeGeometry();
	BuildRenderItems();
	BuildFrameResources();
	BuildDescriptorHeaps();
	BuildConstantBufferViews();
	BuildPSOs();

	// 执行初始化命令
	ThrowIfFailed(mCommandList->Close());
	ID3D12CommandList* CmdList[] = {mCommandList.Get()};
	mCommandQueue->ExecuteCommandLists(_countof(CmdList), CmdList);

	// 等待初始化完成
	FlushCommandQueue();
	
    return true;
}

void FEngine::Tick()
{
    std::cout << "Hello Kite!" << std::endl;

	MSG Message = {0};

	mTimer.Reset();

	while (Message.message != WM_QUIT)
	{
		// 如果有窗口消息就进行处理
		if (PeekMessage(&Message, 0, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&Message);
			DispatchMessage(&Message);
		}
		else
		{
			mTimer.Tick();

			if (!bEnginePaused)
			{
				CalculateFrameStats();
				Update(mTimer);
				Draw(mTimer);
			}
			else
			{
				Sleep(100);
			}
		}
	}

	if (Message.wParam != 0)
	{
		std::cerr << "Invalid quit!" << std::endl;
	}
}

void FEngine::Release()
{
    if (!bInitialized)
    {
        return;
    }

    std::cout << "Goodbye Kite" << std::endl;
}

void FEngine::CreateRtvAndDsvDescriptorHeaps()
{
	D3D12_DESCRIPTOR_HEAP_DESC RtvHeapDesc;
	RtvHeapDesc.NumDescriptors = SwapChainBufferCount;
	RtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
	RtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
	RtvHeapDesc.NodeMask = 0;
	ThrowIfFailed(mD3DDevice->CreateDescriptorHeap(&RtvHeapDesc, IID_PPV_ARGS(mRtvHeap.GetAddressOf())));

	D3D12_DESCRIPTOR_HEAP_DESC DsvHeapDesc;
	DsvHeapDesc.NumDescriptors = 1;
	DsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
	DsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
	DsvHeapDesc.NodeMask = 0;
	ThrowIfFailed(mD3DDevice->CreateDescriptorHeap(&DsvHeapDesc, IID_PPV_ARGS(mDsvHeap.GetAddressOf())));
}

void FEngine::OnResize()
{
	assert(mD3DDevice);
	assert(mSwapChain);
	assert(mCommandListAlloc);

	// 在修改资源之前先清空命令队列
	FlushCommandQueue();

	ThrowIfFailed(mCommandList->Reset(mCommandListAlloc.Get(), nullptr));

	// 释放之前的资源，我们会创建新的
	for (int i = 0; i < SwapChainBufferCount; i++)
	{
		mSwapChainBuffer[i].Reset();
	}
	mDepthStencilBuffer.Reset();

	// 修改交换链的大小
	ThrowIfFailed(mSwapChain->ResizeBuffers(SwapChainBufferCount, mWindowWidth, mWindowHeight,
		mBackBufferFormat, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH));

	mCurBackBuffer = 0;

	CD3DX12_CPU_DESCRIPTOR_HANDLE RtvHeapHandle(mRtvHeap->GetCPUDescriptorHandleForHeapStart());
	for (UINT i = 0; i < SwapChainBufferCount; i++)
	{
		ThrowIfFailed(mSwapChain->GetBuffer(i, IID_PPV_ARGS(&mSwapChainBuffer[i])));
		mD3DDevice->CreateRenderTargetView(mSwapChainBuffer[i].Get(), nullptr, RtvHeapHandle);
		RtvHeapHandle.Offset(1, mRtvDescriptorSize);
	}

	// 创建深度/模板缓冲区及其视图
	D3D12_RESOURCE_DESC DepthStencilDesc;
	DepthStencilDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
	DepthStencilDesc.Alignment = 0;
	DepthStencilDesc.Width = mWindowWidth;
	DepthStencilDesc.Height = mWindowHeight;
	DepthStencilDesc.DepthOrArraySize = 1;
	DepthStencilDesc.MipLevels = 1;
	DepthStencilDesc.Format = DXGI_FORMAT_R24G8_TYPELESS;
	DepthStencilDesc.SampleDesc.Count = b4xMSAAState ? 4 : 1;
	DepthStencilDesc.SampleDesc.Quality = b4xMSAAState ? (m4xMSAAQuality - 1) : 0;
	DepthStencilDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
	DepthStencilDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;

	D3D12_CLEAR_VALUE OptClear;
	OptClear.Format = mDepthStencilFormat;
	OptClear.DepthStencil.Depth = 1.0f;
	OptClear.DepthStencil.Stencil = 0;

	D3D12_HEAP_PROPERTIES HeapProps = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
	ThrowIfFailed(mD3DDevice->CreateCommittedResource(&HeapProps, D3D12_HEAP_FLAG_NONE,
		&DepthStencilDesc, D3D12_RESOURCE_STATE_COMMON, &OptClear, IID_PPV_ARGS(mDepthStencilBuffer.GetAddressOf())));

	// 利用此资源的格式，为整个资源的第0 mip层创建描述符
	D3D12_DEPTH_STENCIL_VIEW_DESC DepthStencilViewDesc;
	DepthStencilViewDesc.Flags = D3D12_DSV_FLAG_NONE;
	DepthStencilViewDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
	DepthStencilViewDesc.Format = mDepthStencilFormat;
	DepthStencilViewDesc.Texture2D.MipSlice = 0;
	mD3DDevice->CreateDepthStencilView(mDepthStencilBuffer.Get(), &DepthStencilViewDesc, DepthStencilView());

	// 将资源从初始状态转换为深度缓冲区
	D3D12_RESOURCE_BARRIER DepthStencilBarrier = CD3DX12_RESOURCE_BARRIER::Transition(mDepthStencilBuffer.Get(),
		D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_DEPTH_WRITE);
	mCommandList->ResourceBarrier(1, &DepthStencilBarrier);

	// 执行调整大小的命令
	ThrowIfFailed(mCommandList->Close());
	ID3D12CommandList* CmdLists[]= { mCommandList.Get() };
	mCommandQueue->ExecuteCommandLists(_countof(CmdLists), CmdLists);

	// 等待大小调整结束
	FlushCommandQueue();

	// 更新视口的变换来覆盖窗口区域
	mScreenViewport.TopLeftX = 0;
	mScreenViewport.TopLeftY = 0;
	mScreenViewport.Width = static_cast<float>(mWindowWidth);
	mScreenViewport.Height = static_cast<float>(mWindowHeight);
	mScreenViewport.MinDepth = 0.0f;
	mScreenViewport.MaxDepth = 1.0f;

	mScissorRect = {0, 0, mWindowWidth, mWindowHeight};

	// 若用户调整了窗口尺寸，则更新纵横比并重新计算投影矩阵
	DirectX::XMMATRIX ProjMatrix = DirectX::XMMatrixPerspectiveFovLH(0.25f*MathHelper::Pi,
		GetAspecRatio(), 1.0f, 1000.0f);
	XMStoreFloat4x4(&mProj, ProjMatrix);
}

void FEngine::Update(const GameTimer& InGameTimer)
{
	OnKeyboardInput(InGameTimer);
	UpdateCamera(InGameTimer);
	
	// 循环往复地获取帧资源循环数组中的元素
	mCurFrameResourceIndex = (mCurFrameResourceIndex + 1) % GNumFrameResources;
	mCurFrameResource = mFrameResources[mCurFrameResourceIndex].get();

	// GPU端是否已经执行完处理当前帧资源的所有命令呢？
	// 如果还没有就令CPU等待，直到GPU完成命令的执行并抵达这个围栏点
	if (mCurFrameResource->mFence != 0 && mFence->GetCompletedValue() < mCurFrameResource->mFence)
	{
		HANDLE EventHandle = CreateEventEx(nullptr, nullptr, false, EVENT_ALL_ACCESS);
		ThrowIfFailed(mFence->SetEventOnCompletion(mCurFrameResource->mFence, EventHandle));
		WaitForSingleObject(EventHandle, INFINITE);
		CloseHandle(EventHandle);
	}

	UpdateObjectCBs(InGameTimer);
	UpdateMainPassCB(InGameTimer);
}

void FEngine::Draw(const GameTimer& InGameTimer)
{
	auto CmdListAlloc = mCurFrameResource->mCmdListAlloc;
	
	// 重复使用记录命令的相关内存
	// 只有当与GPU关联的命令列表执行完成时，我们才能将其重置
	ThrowIfFailed(CmdListAlloc->Reset());

	// 在通过ExecuteCommandList方法将命令列表添加到命令队列中之后，我们就可以对它进行重置
	// 复用命令列表即复用与之相关的内存
	if (mIsWireframe)
	{
		ThrowIfFailed(mCommandList->Reset(CmdListAlloc.Get(), mPSOs["Opaque_Wireframe"].Get()));
	}
	else
	{
		ThrowIfFailed(mCommandList->Reset(CmdListAlloc.Get(), mPSOs["Opaque"].Get()));
	}

	// 设置视口和裁剪矩形。它们需要随着命令列表的重置而重置
	mCommandList->RSSetViewports(1, &mScreenViewport);
	mCommandList->RSSetScissorRects(1, &mScissorRect);

	// 对资源的状态进行转换，将资源从呈现状态转换为渲染目标状态
	D3D12_RESOURCE_BARRIER ResourceBarrier = CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
		D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
	mCommandList->ResourceBarrier(1, &ResourceBarrier);

	// 清除后台缓冲区和深度缓冲区
	mCommandList->ClearRenderTargetView(CurrentBackBufferView(), DirectX::Colors::LightSteelBlue, 0, nullptr);
	mCommandList->ClearDepthStencilView(DepthStencilView(), D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);

	// 指定将要渲染的缓冲区
	D3D12_CPU_DESCRIPTOR_HANDLE CurRenderTarget = CurrentBackBufferView();
	D3D12_CPU_DESCRIPTOR_HANDLE CurDepthStencil = DepthStencilView();
	mCommandList->OMSetRenderTargets(1, &CurRenderTarget, true, &CurDepthStencil);

	ID3D12DescriptorHeap* DescriptorHeaps[] = { mCbvHeap.Get() };
	mCommandList->SetDescriptorHeaps(_countof(DescriptorHeaps), DescriptorHeaps);

	mCommandList->SetGraphicsRootSignature(mRootSignature.Get());

	int PassCbvIndex = mPassCbvOffset + mCurFrameResourceIndex;
	auto PassCbvHandle = CD3DX12_GPU_DESCRIPTOR_HANDLE(mCbvHeap->GetGPUDescriptorHandleForHeapStart());
	PassCbvHandle.Offset(PassCbvIndex, mCbvSrvUavDescriptorSize);
	mCommandList->SetGraphicsRootDescriptorTable(1, PassCbvHandle);

	DrawRenderItems(mCommandList.Get(), mOpaqueRenderItems);
	
	// 再次对资源状态进行转换，将资源从渲染目标状态转换回呈现状态
	ResourceBarrier = CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);

	// 完成命令的记录
	ThrowIfFailed(mCommandList->Close());

	// 将待执行的命令列表加入命令队列
	ID3D12CommandList* CmdLists[] = { mCommandList.Get() };
	mCommandQueue->ExecuteCommandLists(_countof(CmdLists), CmdLists);

	// 交换后台缓冲区和前台缓冲区
	ThrowIfFailed(mSwapChain->Present(0, 0));
	mCurBackBuffer = (mCurBackBuffer + 1) % SwapChainBufferCount;

	// 增加围栏值，将命令标记到此围栏点
	mCurFrameResource->mFence = ++mCurrentFence;

	// 向命令队列添加一条指令来设置一个新的围栏点
	// 由于当前的GPU正在执行绘制命令，所以在GPU处理完Signal()函数之前的所有命令以前，
	// 并不会设置此新的围栏点
	mCommandQueue->Signal(mFence.Get(), mCurrentFence);
}

void FEngine::OnMouseDown(WPARAM btnState, int x, int y)
{
	mLastMousePos.x = x;
	mLastMousePos.y = y;
}

void FEngine::OnMouseUp(WPARAM btnState, int x, int y)
{
	ReleaseCapture();
}

void FEngine::OnMouseMove(WPARAM btnState, int x, int y)
{
	if ((btnState & MK_LBUTTON) != 0)
	{
		// 根据鼠标的移动距离计算旋转角度，并令每个像素都按此角度的1/4旋转
		float Dx = DirectX::XMConvertToRadians(0.25f*static_cast<float>(x - mLastMousePos.x));
		float Dy = DirectX::XMConvertToRadians(0.25f*static_cast<float>(y - mLastMousePos.y));

		// 根据鼠标的输入来更新摄像机绕立方体旋转的角度
		mTheta += Dx;
		mPhi += Dy;

		// 限制角度mPhi的范围
		mPhi = MathHelper::Clamp(mPhi, 0.1f, MathHelper::Pi - 0.1f);
	}
	else if((btnState & MK_RBUTTON) != 0)
	{
		// 使得每个像素对应场景中0.2单位
		float Dx = 0.05f*static_cast<float>(x - mLastMousePos.x);
		float Dy = 0.05f*static_cast<float>(y - mLastMousePos.y);

		// 根据鼠标的输入来更新摄像机的半径
		mRadius += Dx - Dy;

		// 限制半径的范围
		mRadius = MathHelper::Clamp(mRadius, 5.0f, 150.0f);
	}
	
	mLastMousePos.x = x;
	mLastMousePos.y = y;
}

float FEngine::GetAspecRatio() const
{
	return static_cast<float>(mWindowWidth) / mWindowHeight;
}

bool FEngine::InitMainWindow()
{
    WNDCLASS WindowClass;
    WindowClass.style = CS_HREDRAW | CS_VREDRAW;
    WindowClass.lpfnWndProc = MainWndProc;
	WindowClass.cbClsExtra = 0;
	WindowClass.cbWndExtra = 0;
	WindowClass.hInstance = mAppInst;
	WindowClass.hIcon = LoadIcon(0, IDI_APPLICATION);
	WindowClass.hCursor = LoadCursor(0, IDC_ARROW);
	WindowClass.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
	WindowClass.lpszMenuName = 0;
	WindowClass.lpszClassName = L"KiteEngine";

	if (!RegisterClass(&WindowClass))
	{
		MessageBox(0, L"RegisterClass Failed", L"Error", 0);
		return false;
	}

	RECT WindowRect = {0, 0, mWindowWidth, mWindowHeight};
	AdjustWindowRect(&WindowRect, WS_OVERLAPPEDWINDOW, false);
	int Width = WindowRect.right - WindowRect.left;
	int Height = WindowRect.bottom - WindowRect.top;

	mMainWnd = CreateWindow(L"KiteEngine", mMainWndCaption.c_str(),WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, Width, Height, 0, 0, mAppInst, 0);
	if (!mMainWnd)
	{
		MessageBox(0, L"CreateWindow Failed.", L"Error", 0);
		return false;
	}

	ShowWindow(mMainWnd, SW_SHOW);
	UpdateWindow(mMainWnd);

	return true;
}

bool FEngine::InitDirect3D()
{
#if defined(DEBUG) || defined(_DEBUG)
	{
		ComPtr<ID3D12Debug> DebugController;
		ThrowIfFailed(D3D12GetDebugInterface(IID_PPV_ARGS(&DebugController)));
		DebugController->EnableDebugLayer();
	}
#endif

	ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&mDxgiFactory)));

	// 尝试创建硬件设备
	HRESULT HardwareResult = D3D12CreateDevice(
		nullptr, // 默认适配器
		D3D_FEATURE_LEVEL_12_0,
		IID_PPV_ARGS(&mD3DDevice));

	if (FAILED(HardwareResult))
	{
		// 回退至WARP设备
		ComPtr<IDXGIAdapter> pWarpAdapter;
		ThrowIfFailed(mDxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(&pWarpAdapter)));

		ThrowIfFailed(D3D12CreateDevice(pWarpAdapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&mD3DDevice)));
	}

	ThrowIfFailed(mD3DDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)));

	mRtvDescriptorSize = mD3DDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
	mDsvDescriptorSize = mD3DDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
	mCbvSrvUavDescriptorSize = mD3DDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

	// 检测对4X SMAA质量级别的支持
	D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS QualityLevels;
	QualityLevels.Format = mBackBufferFormat;
	QualityLevels.SampleCount = 4;
	QualityLevels.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;
	QualityLevels.NumQualityLevels = 0;
	ThrowIfFailed(mD3DDevice->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &QualityLevels, sizeof(QualityLevels)));

	m4xMSAAQuality = QualityLevels.NumQualityLevels;;
	assert(m4xMSAAQuality > 0 && "Unexpected MSAA quality level.");

	CreateCommandObjects();
	CreateSwapChain();
	CreateRtvAndDsvDescriptorHeaps();
	
	return true;
}

void FEngine::CreateCommandObjects()
{
	D3D12_COMMAND_QUEUE_DESC QueueDesc = {};
	QueueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
	QueueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
	ThrowIfFailed(mD3DDevice->CreateCommandQueue(&QueueDesc, IID_PPV_ARGS(&mCommandQueue)));

	ThrowIfFailed(mD3DDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(mCommandListAlloc.GetAddressOf())));

	ThrowIfFailed(mD3DDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT,
		mCommandListAlloc.Get(), // 关联命令分配器
		nullptr, // 初始化流水线状态对象
		IID_PPV_ARGS(mCommandList.GetAddressOf())));

	// 首先要将命令列表置于关闭状态。这是因为在第一次引用命令列表时，我们要对它进行重置，而在调用重置方法之前又需要先将其关闭，
	mCommandList->Close();
}

void FEngine::CreateSwapChain()
{
	// 释放之前所创的交换链，随后再进行创建
	mSwapChain.Reset();

	DXGI_SWAP_CHAIN_DESC SwapChainDesc = {};
	SwapChainDesc.BufferDesc.Width = mWindowWidth;
	SwapChainDesc.BufferDesc.Height = mWindowHeight;
	SwapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
	SwapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
	SwapChainDesc.BufferDesc.Format = mBackBufferFormat;
	SwapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
	SwapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
	SwapChainDesc.SampleDesc.Count = b4xMSAAState ? 4 : 1;
	SwapChainDesc.SampleDesc.Quality = b4xMSAAState ? (m4xMSAAQuality - 1) : 0;
	SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	SwapChainDesc.BufferCount = SwapChainBufferCount;
	SwapChainDesc.OutputWindow = mMainWnd;
	SwapChainDesc.Windowed = true;
	SwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
	SwapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

	// 交换链需要通过命令队列对其进行刷新
	ThrowIfFailed(mDxgiFactory->CreateSwapChain(mCommandQueue.Get(), &SwapChainDesc, &mSwapChain));
}

void FEngine::FlushCommandQueue()
{
	// 增加围栏值，接下来将命令标记到此围栏点
	mCurrentFence++;

	// 向命令队列中添加一条用来设置新围栏点的命令
	// 由于这条命令要交由GPU处理(即由GPU端来修改围栏值)，所以在GPU处理完命令队列中此Signal()
	// 以前的所有命令之前，它并不会设置新的围栏点
	// ID3D12CommandQueue::Signal方法从GPU端设置围栏值，而ID3D12Fence::Signal方法则从CPU端设置围栏值
	ThrowIfFailed(mCommandQueue->Signal(mFence.Get(), mCurrentFence));

	// 在CPU端等待GPU，直到后者执行完这个围栏点之前的所有命令
	if (mFence->GetCompletedValue() < mCurrentFence)
	{
		HANDLE EventHandle = CreateEventEx(nullptr, 0, false, EVENT_ALL_ACCESS);

		// 若GPU命中当前围栏(即直行道Signal()指令，修改了围栏值)，则激发预定事件
		ThrowIfFailed(mFence->SetEventOnCompletion(mCurrentFence, EventHandle));

		// 等待GPU命中围栏，激发事件
		WaitForSingleObject(EventHandle, INFINITE);
		CloseHandle(EventHandle);
	}
}

ID3D12Resource* FEngine::CurrentBackBuffer() const
{
	return mSwapChainBuffer[mCurBackBuffer].Get();
}

D3D12_CPU_DESCRIPTOR_HANDLE FEngine::CurrentBackBufferView() const
{
	return CD3DX12_CPU_DESCRIPTOR_HANDLE(mRtvHeap->GetCPUDescriptorHandleForHeapStart(), mCurBackBuffer, mRtvDescriptorSize);
}

D3D12_CPU_DESCRIPTOR_HANDLE FEngine::DepthStencilView() const
{
	return mDsvHeap->GetCPUDescriptorHandleForHeapStart();
}

void FEngine::CalculateFrameStats()
{
	static int FrameCount = 0;
	static float TimeElapsed = 0.0f;

	FrameCount++;

	// 以1秒为统计周期来计算平均帧数以及每帧的平均渲染时间
	if ((mTimer.TotalTime() - TimeElapsed) >= 1.0f)
	{
		float FPS = (float)FrameCount;
		float Ms = 1000.0f / FPS;

		std::wstring FPSStr = std::to_wstring(FPS);
		std::wstring MsStr = std::to_wstring(Ms);

		std::wstring WindowText = mMainWndCaption +
			L"    FPS: " + FPSStr +
			L"     ms: " + MsStr;

		SetWindowText(mMainWnd, WindowText.c_str());

		// 为计算下一组平均值而重置
		FrameCount = 0;
		TimeElapsed += 1.0f;
	}
}

void FEngine::OnKeyboardInput(const GameTimer& InGameTimer)
{
	if (GetAsyncKeyState('1') & 0x8000)
	{
		mIsWireframe = true;
	}
	else
	{
		mIsWireframe = false;
	}
}

void FEngine::UpdateCamera(const GameTimer& InGameTimer)
{
	// 将球坐标转换为笛卡尔坐标
	mEyePos.x = mRadius * sinf(mPhi) * cosf(mTheta);
	mEyePos.y = mRadius * sinf(mPhi) * sinf(mTheta);
	mEyePos.z = mRadius * cosf(mPhi);

	// 构建观察矩阵
	DirectX::XMVECTOR Pos = DirectX::XMVectorSet(mEyePos.x, mEyePos.y, mEyePos.z, 1.0f);
	DirectX::XMVECTOR Target = DirectX::XMVectorZero();
	DirectX::XMVECTOR Up = DirectX::XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);

	DirectX::XMMATRIX View = DirectX::XMMatrixLookAtLH(Pos, Target, Up);
	XMStoreFloat4x4(&mView, View);
}

void FEngine::UpdateObjectCBs(const GameTimer& InGameTimer)
{
	auto CurObjectCB = mCurFrameResource->mObjectCB.get();
	for (auto& Elem : mAllRenderItems)
	{
		// 只要常量发生了改变就得更新常量缓冲区内的数据。而且要对每个帧资源都进行更新
		if (Elem->NumFramesDirty > 0)
		{
			DirectX::XMMATRIX World = XMLoadFloat4x4(&Elem->World);

			ObjectConstants ObjConstants;
			DirectX::XMStoreFloat4x4(&ObjConstants.World, DirectX::XMMatrixTranspose(World));

			CurObjectCB->CopyData(Elem->ObjCBIndex, ObjConstants);

			// 还需要对下一个FrameResource进行更新
			Elem->NumFramesDirty--;
		}
	}
}

void FEngine::UpdateMainPassCB(const GameTimer& InGameTimer)
{
	DirectX::XMMATRIX View = XMLoadFloat4x4(&mView);
	DirectX::XMMATRIX Proj = XMLoadFloat4x4(&mProj);
	DirectX::XMMATRIX ViewProj = DirectX::XMMatrixMultiply(View, Proj);
	
	DirectX::XMVECTOR ViewDeterminant = DirectX::XMMatrixDeterminant(View);
	DirectX::XMMATRIX InvView = DirectX::XMMatrixInverse(&ViewDeterminant, View);
	DirectX::XMVECTOR ProjDeterminant = DirectX::XMMatrixDeterminant(Proj);
	DirectX::XMMATRIX InvProj = DirectX::XMMatrixInverse(&ProjDeterminant, Proj);
	DirectX::XMVECTOR ViewProjDeterminant = DirectX::XMMatrixDeterminant(ViewProj);
	DirectX::XMMATRIX InvViewProj = DirectX::XMMatrixInverse(&ViewProjDeterminant, ViewProj);

	DirectX::XMStoreFloat4x4(&mMainPassCB.View, DirectX::XMMatrixTranspose(View));
	DirectX::XMStoreFloat4x4(&mMainPassCB.InvView, DirectX::XMMatrixTranspose(InvView));
	DirectX::XMStoreFloat4x4(&mMainPassCB.Proj, DirectX::XMMatrixTranspose(Proj));
	DirectX::XMStoreFloat4x4(&mMainPassCB.InvProj, DirectX::XMMatrixTranspose(InvProj));
	DirectX::XMStoreFloat4x4(&mMainPassCB.ViewProj, DirectX::XMMatrixTranspose(ViewProj));
	DirectX::XMStoreFloat4x4(&mMainPassCB.InvViewProj, DirectX::XMMatrixTranspose(InvViewProj));
	mMainPassCB.EyePosW = mEyePos;
	mMainPassCB.RenderTargetSize = DirectX::XMFLOAT2((float)mWindowWidth, (float)mWindowHeight);
	mMainPassCB.InvRenderTargetSize = DirectX::XMFLOAT2(1.0f/mWindowWidth, 1.0f/mWindowHeight);
	mMainPassCB.NearZ = 1.0f;
	mMainPassCB.FarZ = 1000.0f;
	mMainPassCB.TotalTime = InGameTimer.TotalTime();
	mMainPassCB.DeltaTime = InGameTimer.DeltaTime();

	auto CurPassCB = mCurFrameResource->mPassCB.get();
	CurPassCB->CopyData(0, mMainPassCB);
}

void FEngine::BuildDescriptorHeaps()
{
	UINT ObjCount = (UINT)mOpaqueRenderItems.size();

	// 我们需要为每个帧资源中的每一个物体都创建一个CBV描述符，
	// 为了容纳每个帧资源中的渲染过程CBV而+1
	UINT NumDescriptors = (ObjCount + 1) * GNumFrameResources;

	// 保存渲染过程CBV的起始偏移量。在本程序中，这是排在最后面的3个描述符
	mPassCbvOffset = ObjCount * GNumFrameResources;
	
	D3D12_DESCRIPTOR_HEAP_DESC CbvHeapDesc;
	CbvHeapDesc.NumDescriptors = NumDescriptors;
	CbvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
	CbvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
	CbvHeapDesc.NodeMask = 0;

	ThrowIfFailed(mD3DDevice->CreateDescriptorHeap(&CbvHeapDesc, IID_PPV_ARGS(&mCbvHeap)));
}

void FEngine::BuildConstantBufferViews()
{
	UINT ObjCBByteSize = D3DUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));

	UINT ObjCount = (UINT)mOpaqueRenderItems.size();

	// 每个帧资源中的每一个物体都需要一个对应的CBV描述符
	for (int FrameIndex = 0; FrameIndex < GNumFrameResources; FrameIndex++)
	{
		auto ObjectCB = mFrameResources[FrameIndex]->mObjectCB->GetResource();
		for (UINT ObjIndex = 0; ObjIndex < ObjCount; ObjIndex++)
		{
			D3D12_GPU_VIRTUAL_ADDRESS CBAddress = ObjectCB->GetGPUVirtualAddress();

			// 偏移到常量缓冲区中第i个物体所对应的常量数据
			CBAddress += ObjIndex * ObjCBByteSize;

			// 偏移到该物体在描述符堆中的CBV
			int HeapIndex = FrameIndex * ObjCount + ObjIndex;
			auto Handle = CD3DX12_CPU_DESCRIPTOR_HANDLE(mCbvHeap->GetCPUDescriptorHandleForHeapStart());
			Handle.Offset(HeapIndex, mCbvSrvUavDescriptorSize);

			D3D12_CONSTANT_BUFFER_VIEW_DESC CbvDesc;
			CbvDesc.BufferLocation = CBAddress;
			CbvDesc.SizeInBytes = ObjCBByteSize;

			mD3DDevice->CreateConstantBufferView(&CbvDesc, Handle);
		}
	}

	UINT PassCBByteSize = D3DUtil::CalcConstantBufferByteSize(sizeof(PassConstants));

	// 最后3个描述符依次是每个帧资源的渲染过程CBV
	for (int FrameIndex = 0; FrameIndex < GNumFrameResources; FrameIndex++)
	{
		auto PassCB = mFrameResources[FrameIndex]->mPassCB->GetResource();

		// 每个帧资源的渲染过程缓冲区中只存有一个常量缓冲区
		D3D12_GPU_VIRTUAL_ADDRESS CBAddress = PassCB->GetGPUVirtualAddress();

		// 偏移到描述符堆中对应的渲染过程CBV
		int HeapIndex = mPassCbvOffset + FrameIndex;
		auto Handle = CD3DX12_CPU_DESCRIPTOR_HANDLE(mCbvHeap->GetCPUDescriptorHandleForHeapStart());
		Handle.Offset(HeapIndex, mCbvSrvUavDescriptorSize);

		D3D12_CONSTANT_BUFFER_VIEW_DESC CbvDesc;
		CbvDesc.BufferLocation = CBAddress;
		CbvDesc.SizeInBytes = PassCBByteSize;

		mD3DDevice->CreateConstantBufferView(&CbvDesc, Handle);
	}
}

void FEngine::BuildRootSignature()
{
	// 着色器程序一般需要以资源作为输入(例如常量缓冲区、纹理、采样器等)
	// 根签名则定义了着色器程序所需的具体资源
	// 如果把着色器程序看做一个函数，而将输入的资源当做向函数传递的参数数据，那么便可类似地认为根签名定义的是函数签名

	CD3DX12_DESCRIPTOR_RANGE CbvTable0;
	CbvTable0.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0);
	
	CD3DX12_DESCRIPTOR_RANGE CbvTable1;
	CbvTable1.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 1);

	// 根参数可以使描述符表、根描述符或根常量
	CD3DX12_ROOT_PARAMETER SlotRootParameter[2];

	// 创建跟CBT
	SlotRootParameter[0].InitAsDescriptorTable(1, &CbvTable0);
	SlotRootParameter[1].InitAsDescriptorTable(1, &CbvTable1);

	// 根签名由一组根参数构成
	CD3DX12_ROOT_SIGNATURE_DESC RootSigDesc(2, SlotRootParameter, 0,
		nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

	// 用单个寄存器槽来创建一个根签名，该槽位指向一个进含有单个常量缓冲区的描述符区域
	ComPtr<ID3DBlob> SerializedRootSig = nullptr;
	ComPtr<ID3DBlob> ErrorBlob = nullptr;
	HRESULT HRes = D3D12SerializeRootSignature(&RootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
		SerializedRootSig.GetAddressOf(), ErrorBlob.GetAddressOf());

	if (ErrorBlob != nullptr)
	{
		OutputDebugStringA((char*)ErrorBlob->GetBufferPointer());
	}
	ThrowIfFailed(HRes);

	ThrowIfFailed(mD3DDevice->CreateRootSignature(
		0,
		SerializedRootSig->GetBufferPointer(),
		SerializedRootSig->GetBufferSize(),
		IID_PPV_ARGS(&mRootSignature)));
}

void FEngine::BuildInputLayout()
{
	mInputLayout = {
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
		{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}
	};
}

void FEngine::BuildShapeGeometry()
{
	FGeometryGenerator GeoGen;
	FGeometryGenerator::MeshData Box = GeoGen.CreateBox(1.5f, 0.5f, 1.5f, 3);
	FGeometryGenerator::MeshData Grid = GeoGen.CreateGrid(20.0f, 30.0f, 60, 40);
	FGeometryGenerator::MeshData Sphere = GeoGen.CreateSphere(0.5f, 20, 20);
	FGeometryGenerator::MeshData Cylinder = GeoGen.CreateCylinder(0.5f, 0.3f, 3.f, 20, 20);

	//
	// 将所有的几何体数据都合并到一对大的顶点/索引缓冲区中
	// 以此来定义每个子网格数据在缓冲区中所占的范围
	//

	// 对合并顶点缓冲区中每个物体的顶点偏移量进行缓存
	UINT BoxVertexOffset = 0;
	UINT GridVertexOffset = (UINT)Box.Vertices.size();
	UINT SphereVertexOffset = GridVertexOffset + (UINT)Grid.Vertices.size();
	UINT CylinderVertexOffset = SphereVertexOffset + (UINT)Sphere.Vertices.size();

	// 对合并索引缓冲区中每个物体的起始索引进行缓存
	UINT BoxIndexOffset = 0;
	UINT GridIndexOffset = (UINT)Box.Indices32.size();
	UINT SphereIndexOffset = GridIndexOffset + (UINT)Grid.Indices32.size();
	UINT CylinderIndexOffset = SphereIndexOffset + (UINT)Sphere.Indices32.size();

	// 定义的多个SubmeshGeometry结构体中包含了顶点/索引缓冲区内不同几何体的子网格数据

	FSubmeshGeometry BoxSubmesh;
	BoxSubmesh.IndexCount = (UINT)Box.Indices32.size();
	BoxSubmesh.StartIndexLocation = BoxIndexOffset;
	BoxSubmesh.BaseVertexLocation = BoxIndexOffset;

	FSubmeshGeometry GridSubmesh;
	GridSubmesh.IndexCount = (UINT)Grid.Indices32.size();
	GridSubmesh.StartIndexLocation = GridIndexOffset;
	GridSubmesh.BaseVertexLocation = GridVertexOffset;

	FSubmeshGeometry SphereSubmesh;
	SphereSubmesh.IndexCount = (UINT)Sphere.Indices32.size();
	SphereSubmesh.StartIndexLocation = SphereIndexOffset;
	SphereSubmesh.BaseVertexLocation = SphereVertexOffset;

	FSubmeshGeometry CylinderSubmesh;
	CylinderSubmesh.IndexCount = (UINT)Cylinder.Indices32.size();
	CylinderSubmesh.StartIndexLocation = CylinderIndexOffset;
	CylinderSubmesh.BaseVertexLocation = CylinderVertexOffset;

	//
	// 提取出所需的顶点元素，再将所有网格的顶点装进一个顶点缓冲区
	//

	auto TotalVertexCount = Box.Vertices.size() + Grid.Vertices.size() + Sphere.Vertices.size() + Cylinder.Vertices.size();

	std::vector<FVertex> Vertices(TotalVertexCount);

	UINT CurIndex = 0;
	for (UINT Index = 0; Index < Box.Vertices.size(); Index++, CurIndex++)
	{
		Vertices[CurIndex].Pos = Box.Vertices[Index].Position;
		Vertices[CurIndex].Color = DirectX::XMFLOAT4(DirectX::Colors::DarkGreen);
	}

	for (UINT Index = 0; Index < Grid.Vertices.size(); Index++, CurIndex++)
	{
		Vertices[CurIndex].Pos = Grid.Vertices[Index].Position;
		Vertices[CurIndex].Color = DirectX::XMFLOAT4(DirectX::Colors::ForestGreen);
	}

	for (UINT Index = 0; Index < Sphere.Vertices.size(); Index++, CurIndex++)
	{
		Vertices[CurIndex].Pos = Sphere.Vertices[Index].Position;
		Vertices[CurIndex].Color = DirectX::XMFLOAT4(DirectX::Colors::Crimson);
	}

	for (UINT Index = 0; Index < Cylinder.Vertices.size(); Index++, CurIndex++)
	{
		Vertices[CurIndex].Pos = Cylinder.Vertices[Index].Position;
		Vertices[CurIndex].Color = DirectX::XMFLOAT4(DirectX::Colors::SteelBlue);
	}

	std::vector<std::uint16_t> Indices;
	Indices.insert(Indices.end(), std::begin(Box.GetIndices16()), std::end(Box.GetIndices16()));
	Indices.insert(Indices.end(), std::begin(Grid.GetIndices16()), std::end(Grid.GetIndices16()));
	Indices.insert(Indices.end(), std::begin(Sphere.GetIndices16()), std::end(Sphere.GetIndices16()));
	Indices.insert(Indices.end(), std::begin(Cylinder.GetIndices16()), std::end(Cylinder.GetIndices16()));
	
	const UINT VBByteSize = (UINT)Vertices.size() * sizeof(FVertex);
	const UINT IBByteSize = (UINT)Indices.size() * sizeof(std::uint16_t);

	auto Geometry = std::make_unique<FMeshGeometry>();
	Geometry->Name = "ShapeGeo";

	ThrowIfFailed(D3DCreateBlob(VBByteSize, &Geometry->VertexBufferCPU));
	CopyMemory(Geometry->VertexBufferCPU->GetBufferPointer(), Vertices.data(), VBByteSize);

	ThrowIfFailed(D3DCreateBlob(IBByteSize, &Geometry->IndexBufferCPU));
	CopyMemory(Geometry->IndexBufferCPU->GetBufferPointer(), Indices.data(), IBByteSize);

	Geometry->VertexBufferGPU = D3DUtil::CreateDefaultBuffer(
		mD3DDevice.Get(), mCommandList.Get(),
		Vertices.data(), VBByteSize, Geometry->VertexBufferUploader);

	Geometry->IndexBufferGPU = D3DUtil::CreateDefaultBuffer(
		mD3DDevice.Get(), mCommandList.Get(),
		Indices.data(), IBByteSize, Geometry->IndexBufferUploader);

	Geometry->VertexByteStride = sizeof(FVertex);
	Geometry->VertexBufferByteSize = VBByteSize;
	Geometry->IndexFormat = DXGI_FORMAT_R16_UINT;
	Geometry->IndexBufferByteSize = IBByteSize;

	Geometry->DrawArgs["Box"] = BoxSubmesh;
	Geometry->DrawArgs["Grid"] = GridSubmesh;
	Geometry->DrawArgs["Sphere"] = SphereSubmesh;
	Geometry->DrawArgs["Cylinder"] = CylinderSubmesh;

	mGeometries[Geometry->Name] = std::move(Geometry);
}

void FEngine::BuildRenderItems()
{
	auto BoxRenderItem = std::make_unique<FRenderItem>();
	DirectX::XMStoreFloat4x4(&BoxRenderItem->World, DirectX::XMMatrixScaling(2.0f, 2.0f, 2.0f) * DirectX::XMMatrixTranslation(0.0f, 0.5f, 0.0f));
	BoxRenderItem->ObjCBIndex = 0;
	BoxRenderItem->Geo = mGeometries["ShapeGeo"].get();
	BoxRenderItem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
	BoxRenderItem->IndexCount = BoxRenderItem->Geo->DrawArgs["Box"].IndexCount;
	BoxRenderItem->StartIndexLocation = BoxRenderItem->Geo->DrawArgs["Box"].StartIndexLocation;
	BoxRenderItem->BaseVertexLocation = BoxRenderItem->Geo->DrawArgs["Box"].BaseVertexLocation;
	mAllRenderItems.push_back(std::move(BoxRenderItem));

	auto GridRenderItem = std::make_unique<FRenderItem>();
	GridRenderItem->World = MathHelper::Identity4x4();
	GridRenderItem->ObjCBIndex = 1;
	GridRenderItem->Geo = mGeometries["ShapeGeo"].get();
	GridRenderItem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
	GridRenderItem->IndexCount = GridRenderItem->Geo->DrawArgs["Grid"].IndexCount;
	GridRenderItem->StartIndexLocation = GridRenderItem->Geo->DrawArgs["Grid"].StartIndexLocation;
	GridRenderItem->BaseVertexLocation = GridRenderItem->Geo->DrawArgs["Grid"].BaseVertexLocation;
	mAllRenderItems.push_back(std::move(GridRenderItem));

	UINT ObjCBIndex = 2;
	for (int Index = 0; Index < 5; ++Index)
	{
		auto LeftCylinderRenderItem = std::make_unique<FRenderItem>();
		auto RightCylinderRenderItem = std::make_unique<FRenderItem>();
		auto LeftSphereRenderItem = std::make_unique<FRenderItem>();
		auto RightSphereRenderItem = std::make_unique<FRenderItem>();

		DirectX::XMMATRIX LeftCylinderWorld = DirectX::XMMatrixTranslation(+5.0f, 1.5f, -10.0f + Index * 5.0f);
		DirectX::XMMATRIX RightCylinderWorld = DirectX::XMMatrixTranslation(-5.0f, 1.5f, -10.0f + Index * 5.0f);
		DirectX::XMMATRIX LeftSphereWorld = DirectX::XMMatrixTranslation(-5.0f, 3.5f, -10.0f + Index * 5.0f);
		DirectX::XMMATRIX RightSphereWorld = DirectX::XMMatrixTranslation(+5.0f, 3.5f, -10.0f + Index * 5.0f);

		DirectX::XMStoreFloat4x4(&LeftCylinderRenderItem->World, LeftCylinderWorld);
		LeftCylinderRenderItem->ObjCBIndex = ObjCBIndex++;
		LeftCylinderRenderItem->Geo = mGeometries["ShapeGeo"].get();
		LeftCylinderRenderItem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
		LeftCylinderRenderItem->IndexCount = LeftCylinderRenderItem->Geo->DrawArgs["Cylinder"].IndexCount;
		LeftCylinderRenderItem->StartIndexLocation = LeftCylinderRenderItem->Geo->DrawArgs["Cylinder"].StartIndexLocation;
		LeftCylinderRenderItem->BaseVertexLocation = LeftCylinderRenderItem->Geo->DrawArgs["Cylinder"].BaseVertexLocation;

		DirectX::XMStoreFloat4x4(&RightCylinderRenderItem->World, RightCylinderWorld);
		RightCylinderRenderItem->ObjCBIndex = ObjCBIndex++;
		RightCylinderRenderItem->Geo = mGeometries["ShapeGeo"].get();
		RightCylinderRenderItem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
		RightCylinderRenderItem->IndexCount = RightCylinderRenderItem->Geo->DrawArgs["Cylinder"].IndexCount;
		RightCylinderRenderItem->StartIndexLocation = RightCylinderRenderItem->Geo->DrawArgs["Cylinder"].StartIndexLocation;
		RightCylinderRenderItem->BaseVertexLocation = RightCylinderRenderItem->Geo->DrawArgs["Cylinder"].BaseVertexLocation;

		DirectX::XMStoreFloat4x4(&LeftSphereRenderItem->World, LeftSphereWorld);
		LeftSphereRenderItem->ObjCBIndex = ObjCBIndex++;
		LeftSphereRenderItem->Geo = mGeometries["ShapeGeo"].get();
		LeftSphereRenderItem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
		LeftSphereRenderItem->IndexCount = LeftSphereRenderItem->Geo->DrawArgs["Sphere"].IndexCount;
		LeftSphereRenderItem->StartIndexLocation = LeftSphereRenderItem->Geo->DrawArgs["Sphere"].StartIndexLocation;
		LeftSphereRenderItem->BaseVertexLocation = LeftSphereRenderItem->Geo->DrawArgs["Sphere"].BaseVertexLocation;

		DirectX::XMStoreFloat4x4(&RightSphereRenderItem->World, RightSphereWorld);
		RightSphereRenderItem->ObjCBIndex = ObjCBIndex++;
		RightSphereRenderItem->Geo = mGeometries["ShapeGeo"].get();
		RightSphereRenderItem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
		RightSphereRenderItem->IndexCount = RightSphereRenderItem->Geo->DrawArgs["Sphere"].IndexCount;
		RightSphereRenderItem->StartIndexLocation = RightSphereRenderItem->Geo->DrawArgs["Sphere"].StartIndexLocation;
		RightSphereRenderItem->BaseVertexLocation = RightSphereRenderItem->Geo->DrawArgs["Sphere"].BaseVertexLocation;

		mAllRenderItems.push_back(std::move(LeftCylinderRenderItem));
		mAllRenderItems.push_back(std::move(RightCylinderRenderItem));
		mAllRenderItems.push_back(std::move(LeftSphereRenderItem));
		mAllRenderItems.push_back(std::move(RightSphereRenderItem));
	}

	// 此演示程序中的所有渲染项都是非透明的
	for (auto& Elem : mAllRenderItems)
	{
		mOpaqueRenderItems.push_back(Elem.get());
	}
}

void FEngine::BuildFrameResources()
{
	for (int Index = 0; Index < GNumFrameResources; ++Index)
	{
		mFrameResources.push_back(std::make_unique<FFrameResource>(
			mD3DDevice.Get(), 1, (UINT)mAllRenderItems.size()));
	}
}

void FEngine::BuildPSOs()
{
	//
	// 不透明物体的PSO
	//
	D3D12_GRAPHICS_PIPELINE_STATE_DESC OpaquePSODesc;
	ZeroMemory(&OpaquePSODesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
	OpaquePSODesc.InputLayout = { mInputLayout.data(), (UINT)mInputLayout.size() };
	OpaquePSODesc.pRootSignature = mRootSignature.Get();

	TShaderMapRef<FStandardVS> VertexShader(ShaderMap);
	TShaderMapRef<FOpaquePS> PixelShader(ShaderMap);
	OpaquePSODesc.VS = {
		VertexShader.GetBufferPointer(),
		VertexShader.GetBufferSize()
	};
	OpaquePSODesc.PS = {
		PixelShader.GetBufferPointer(),
		PixelShader.GetBufferSize()
	};
	OpaquePSODesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
	OpaquePSODesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
	OpaquePSODesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
	OpaquePSODesc.SampleMask = UINT_MAX;
	OpaquePSODesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
	OpaquePSODesc.NumRenderTargets = 1;
	OpaquePSODesc.RTVFormats[0] = mBackBufferFormat;
	OpaquePSODesc.SampleDesc.Count = b4xMSAAState ? 4 : 1;
	OpaquePSODesc.SampleDesc.Quality = b4xMSAAState ? (m4xMSAAQuality - 1) : 0;
	OpaquePSODesc.DSVFormat = mDepthStencilFormat;
	ThrowIfFailed(mD3DDevice->CreateGraphicsPipelineState(&OpaquePSODesc, IID_PPV_ARGS(&mPSOs["Opaque"])));

	//
	// 不透明网格物体的PSO
	//
	D3D12_GRAPHICS_PIPELINE_STATE_DESC OpaqueWireframePSODesc = OpaquePSODesc;
	OpaqueWireframePSODesc.RasterizerState.FillMode = D3D12_FILL_MODE_WIREFRAME;
	ThrowIfFailed(mD3DDevice->CreateGraphicsPipelineState(&OpaqueWireframePSODesc, IID_PPV_ARGS(&mPSOs["Opaque_Wireframe"])));
}

void FEngine::DrawRenderItems(ID3D12GraphicsCommandList* InCmdList, const std::vector<FRenderItem*>& InRenderItems)
{
	UINT ObjCBByteSize = D3DUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));
	auto ObjectCB = mCurFrameResource->mObjectCB->GetResource();

	// 对于每个渲染项来说
	for (size_t Index = 0; Index < InRenderItems.size(); ++Index)
	{
		auto RenderItem = InRenderItems[Index];

		D3D12_VERTEX_BUFFER_VIEW VertexBufferView = RenderItem->Geo->GetVertexBufferView();
		InCmdList->IASetVertexBuffers(0, 1, &VertexBufferView);

		D3D12_INDEX_BUFFER_VIEW IndexBufferView = RenderItem->Geo->GetIndexBufferView();
		InCmdList->IASetIndexBuffer(&IndexBufferView);

		InCmdList->IASetPrimitiveTopology(RenderItem->PrimitiveType);

		// 为了绘制当前的帧资源和当前物体，偏移到描述符堆中对应的CBV处
		UINT CbvIndex = mCurFrameResourceIndex * (UINT)mOpaqueRenderItems.size() + RenderItem->ObjCBIndex;
		auto CbvHandle = CD3DX12_GPU_DESCRIPTOR_HANDLE(mCbvHeap->GetGPUDescriptorHandleForHeapStart());
		CbvHandle.Offset(CbvIndex, mCbvSrvUavDescriptorSize);

		InCmdList->SetGraphicsRootDescriptorTable(0, CbvHandle);
		InCmdList->DrawIndexedInstanced(RenderItem->IndexCount, 1, RenderItem->StartIndexLocation, RenderItem->BaseVertexLocation, 0);
	}
}
